package com.thero.core.command;

import com.thero.core.command.annotation.Controller;
import com.thero.core.command.annotation.Handler;
import com.thero.core.net.ProtocolMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.Map;

/**
 * Created by wusk on 2017/11/29.
 * 命令字处理程序扫描工具
 */
public class HandlerScanner {
    private static final Logger logger = LoggerFactory.getLogger(HandlerScanner.class);

    private static final String SCAN_PACKAGE_PATH = "com.thero.core.controller";

    public void scan(Map<Short, HandlerMethod> map) {
        String packagePath = SCAN_PACKAGE_PATH.replace('.', '/');

        //扫描包路径
        Enumeration<URL> dirs = null;
        try{
            dirs = Thread.currentThread().getContextClassLoader().getResources(packagePath);
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("Get controller path fail.", e);
        }
        if(null == dirs){
            return;
        }
        while (dirs.hasMoreElements())
        {
            URL url = dirs.nextElement();

            //得到但钱url的类型
            String protocol = url.getProtocol();

            //如果当前类型是文件类型
            if ("file".equals(protocol))
            {
                try
                {
                    //获取包的物理路径
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    filePath = filePath.substring(1);
                    scanPath(filePath, map);
                }
                catch (UnsupportedEncodingException e)
                {
                    logger.error("Can not get class file path.", e);
                }
            }else{
                logger.info("Illegal path type. {}", url.toString());
            }
        }
    }

    private void scanPath(String filePath, Map<Short, HandlerMethod> map){
        Path dir = Paths.get(filePath);
        try
        {
            //获得当前目录下的文件的stream流
            DirectoryStream<Path> stream = Files.newDirectoryStream(dir);
            for(Path path : stream)
            {
                String fileName = String.valueOf(path.getFileName());

                String className = fileName.substring(0, fileName.length() - 6);

                Class clazz = null;
                try
                {
                    clazz = Thread.currentThread().getContextClassLoader().loadClass(SCAN_PACKAGE_PATH + "." + className);
                }
                catch (ClassNotFoundException e)
                {
                    logger.error("The path is not an class file. {}", path);
                }

                //判断该注解类型是不是所需要的类型
                if (null != clazz && null != clazz.getAnnotation(Controller.class))
                {
                    parseClass(clazz, map);
                }
            }
        }
        catch (IOException e)
        {
            logger.error("Read file fail. file path = {}", filePath, e);
        }
    }

    private void parseClass(Class clazz, Map<Short, HandlerMethod> map)
    {
        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods)
        {
            //得到该类下面的Handler注解
            Handler annotation = method.getAnnotation(Handler.class);
            if (null != annotation)
            {
                short code = annotation.code();
                if(map.containsKey(code)){
                    logger.error("Command code was duplicate. command code = {}", code);
                    return;
                }
                Class[] parameters = method.getParameterTypes();
                if(parameters.length != 2){
                    logger.error("The handler with wrong parameter amount. command code = {}", code);
                    return;
                }
                Class context = parameters[0];
                Class message = parameters[1];
                if(!CommandHandler.class.equals(context)){
                    logger.error("The handler with wrong parameter type of parameter 0. command code = {}", code);
                    return;
                }
                if(!ProtocolMessage.class.equals(message)){
                    logger.error("The handler with wrong parameter type of parameter 1. command code = {}", code);
                    return;
                }
                HandlerMethod handler = new HandlerMethod(code, clazz, method);
                map.put(code, handler);
            }
        }
    }
}
